/* ------------------------------------------------------------
 * Bunny project - Data entry UI
 * Data store
 * ------------------------------------------------------------ 
 */
 
/* How to use me
 ----------------
 
 First call:
   * loadData(String[] lines)		loads the data from an array of strings
 
 Then you can:
 
   * int getRows()				returns numbers of rows
   * int getCols()				returns number of cols
   * ArrayList<Float> getCell(int row, int col)	returns all values stored in cell (r, c)
 
*/

final int SERVER_PORT		= 28013;

import processing.net.*;

class DataStore
{
	public DataStore()
	{
		rows = cols = 0;
		data = null;
		myServer = null;
	}
	
	public int getRows() { return rows; }
	public int getCols() { return cols; }
	
	public void makeEmpty(int _rows, int _cols)
	{
		rows = _rows;
		cols = _cols;
		data = new ArrayList[rows][cols];
		
		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				data[i][j] = new ArrayList<Float>();
			}
		}
	}
	
	public void startServer(PApplet app)
	{
		myServer = new Server(app, SERVER_PORT); 
	}
	
	public boolean readFromNetwork()
	{
		if (myServer == null) { 
                  return false; 
                } 
                Client c = myServer.available();
		if (c != null)
		{
			String whatClientSaid = c.readString();
			String[] _data = split(whatClientSaid, "\n");
			println(" ---> network read " + _data.length);
			
			loadData( _data );
			return true;
		}
		else
		{
			// no connection, nothing updated
			return false;
		}
	}
	
	public void sendToNetwork(PApplet app, String serverAddress)
	{
		// make one big string
		String[] _data = saveData();
		String allData = "";
		for (int i = 0; i < _data.length; i++)
		{
			allData += _data[i];
			if (i < _data.length-1) {
				allData += "\n";
			}
		}
		
		// send data to server
		Client myClient = new Client(app, serverAddress, SERVER_PORT);
		myClient.write(allData);
		myClient.stop();
	}
	
	
	public boolean loadData(String[] lines)
	{
		// parse data
		boolean header = false;
		int curRow = 0;
		int curCol = 0;
		 
		// loop through all lines
		for (int l = 0; l < lines.length; l++)
		{
			if (lines[l] == null) {
				// ignore nulls
				continue;
			}
			
			String curLine = trim(lines[l]);
			if (curLine.length() == 0 || curLine.charAt(0) == '#')
			{
				// ignore empty lines or comments (begins with '#')
				continue;
			}
			
			if (!header)
			{
				// expect header of file
				String tokens[] = split( curLine.toLowerCase(), "x" );
				if (tokens.length != 2) 
				{
					println("Parse error at line " + (l+1) + ": invalid matrix size in first line of file");
					return false;
				}
				else
				{
					int r = Integer.parseInt( trim(tokens[0]) );
					int c = Integer.parseInt( trim(tokens[1]) );
					makeEmpty(r, c);
					header = true;
				}	
			}
			else
			{
				if (curRow >= rows)
				{
					println("Parse error at line " + (l+1) + ": number of rows exceeding expected matrix size of " + rows + " X " + cols);
					return false;
				}
				
				if (!fillCell( getCell(curRow, curCol), curLine ))
				{
					return false;
				}
				
				curCol++;
				if (curCol == cols)
				{
					curRow++;
					curCol = 0;
				}
			}
		}
		return true;
	}
	
	public boolean loadFile(String filename)
	{
		String[] lines = loadStrings(filename);
		if (lines == null)
		{
			println("Could not read " + filename);
			return false;
		}
		else
		{
			return loadData( lines );
		}
	}
	
	public boolean saveFile(String filename)
	{
		saveStrings( filename, saveData() );
		return true;
	}
	
	public String[] saveData()
	{
		ArrayList<String> lines = new ArrayList<String>();
		String l = null;
		
		// write header
		l = "" + rows + " x " + cols;
		lines.add(l);
		
		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				String c = "";
				ArrayList<Float> cell = getCell(i, j);
				for (int k = 0; k < cell.size(); k++)
				{
					c += cell.get(k);
					if (k < cell.size()-1) {
						c += ", ";
					}
				}
				
				lines.add( "" + i + ", " + j + ": " + c );
			}
		}
		
		// convert to String[]
		String[] fileData = new String[lines.size()];
		for (int i = 0; i < lines.size(); i++) {
			fileData[i] = lines.get(i);
		}
				
		return fileData; 
	}
		
	public ArrayList<Float> getCell(int row, int col)
	{
		if (data == null)
		{
			return null;
		}
		else
		{
			return data[row][col];
		}
	}
	
	public void printData()
	{
		String[] lines = saveData();
		for (int i = 0; i < lines.length; i++)
		{
			println(lines[i]);
		}
	}
	
		
	/* Private
	 * ------------------- 
	 */
	
	// network
	Server myServer;
	
	// size of data matrix
	private int rows;
	private int cols;
	
	// actual data stored here
	private ArrayList<Float> data[][];
	
	private boolean fillCell(ArrayList<Float> cell, String _data)
	{
		String tokens[] = split(_data, ":");
		if (tokens.length != 2)
		{
			println("Parse error: " + _data);
			return false;
		}
		
		// fill in the values
		String index = trim( tokens[0] );
		String cellData = tokens[1] == null ? null : trim(tokens[1]);
		
		if (cellData != null && cellData.length() > 0)
		{
			String values[] = split( cellData, "," );	
			for (int i = 0; i < values.length; i++)
			{			
				float x = Float.parseFloat(trim(values[i]));
				cell.add( x );
			}
		}
		
		return true;
	}
}

float average(ArrayList<Float> cell)
{
	if (cell.size() == 0) {
		return 0.0;
	}
	
	float avg = 0.0;
	for (int i = 0; i < cell.size(); i++) {
		avg += cell.get(i);
	}
	
	return avg / (float) cell.size();
}

